package com.stars.easyms.redis.interceptor;

import com.stars.easyms.base.util.GenericTypeUtil;
import com.stars.easyms.base.util.ReflectUtil;
import com.stars.easyms.redis.annotion.RedisListRange;
import com.stars.easyms.redis.bean.RedisIndexMap;
import com.stars.easyms.redis.enums.RedisListPushType;
import com.stars.easyms.redis.exception.RedisInterceptorException;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>className: RedisListRangeInterceptor</p>
 * <p>description: RedisListRange注解拦截器</p>
 *
 * @author jinzhilong
 * @date 2019-12-24 17:44
 */
public class RedisListRangeInterceptor extends AbstractRedisInterceptor<RedisListRange> {

    @Override
    Object invoke(MethodInvocation methodInvocation, RedisListRange redisListRange) throws RedisInterceptorException {
        Object result = null;
        if (isEnabled()) {
            Method pointMethod = methodInvocation.getMethod();
            Class<?> returnType = pointMethod.getReturnType();
            // range是一个范围，返回值必须是一个List
            boolean isInvalidReturnType = !List.class.isAssignableFrom(returnType);
            if (isInvalidReturnType) {
                logger.error("Method '{}' returnType should be List<T>!", ReflectUtil.getMethodFullName(pointMethod));
                return proceed(methodInvocation);
            }
            // 获取redis的index，如果start大于end则直接返回空列表，如果相等则返回只含有一个对象的list
            String redisKey = getRedisKey(methodInvocation, redisListRange.group());
            RedisIndexMap redisIndexMap = getRedisIndexMap(methodInvocation);
            Long redisIndexStart = redisIndexMap.getIndexStart();
            Long redisIndexEnd = redisIndexMap.getIndexEnd();
            if (redisIndexStart > redisIndexEnd) {
                return Collections.emptyList();
            }

            Class<?> genericReturnClass = null;
            Type genericReturnType = pointMethod.getGenericReturnType();
            if (genericReturnType instanceof ParameterizedType) {
                genericReturnClass = GenericTypeUtil.getGenericClass(genericReturnType, 0);
            }
            result = range(redisKey, redisIndexStart, redisIndexEnd, genericReturnClass);
            boolean override = redisListRange.override();
            if (!CollectionUtils.isEmpty((List) result)) {
                // 去除结果集合为true
                if (override) {
                    easyMsRedisTemplate.listLeftTrim(redisKey, redisIndexStart, redisIndexEnd);
                }
                Long expire = getExpire(methodInvocation, redisListRange.expire());
                boolean isSetExpire = expire != null && expire > 0 && easyMsRedisTemplate.getExpire(redisKey, TimeUnit.MILLISECONDS) <= 0;
                if (isSetExpire) {
                    easyMsRedisTemplate.expire(redisKey, expire, TimeUnit.MILLISECONDS);
                }
                return result;
            }
            // List存在区间获取的 如果结果值不存在  则需要判断缓存中是否redisKey
            boolean existsFlag = easyMsRedisTemplate.hasKey(redisKey);
            if (!existsFlag) {
                // 结果不存在的时候
                result = proceed(methodInvocation);
                if (null != result) {
                    RedisListPushType type = redisListRange.type();
                    Long expire = getExpire(methodInvocation, redisListRange.expire());
                    result = dealMethodResultList((List<?>) result, type, redisKey, genericReturnClass,
                            redisIndexStart, redisIndexEnd, expire, override);
                }
            }
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    private Object dealMethodResultList(List<?> result, RedisListPushType type, String redisKey, Class<?> genericReturnClass,
                                        Long redisIndexStart, Long redisIndexEnd, Long expire, Boolean override) {
        // type L:代表是从左存入到LIST中 R:代表是从右边存到LIST中
        if (RedisListPushType.LEFT == type) {
            if (String.class.equals(genericReturnClass)) {
                easyMsRedisTemplate.listLeftPushAll(redisKey, (List<String>) result);
                result = easyMsRedisTemplate.listLeftRange(redisKey, redisIndexStart, redisIndexEnd);
            } else {
                List<String> strResultList = result.stream()
                        .map(easyMsRedisTemplate::getValueStr)
                        .collect(Collectors.toList());
                easyMsRedisTemplate.listLeftPushAll(redisKey, strResultList);
                result = range(redisKey, redisIndexStart, redisIndexEnd, genericReturnClass);
            }
        } else {
            if (String.class.equals(genericReturnClass)) {
                easyMsRedisTemplate.listRightPushAll(redisKey, (List<String>) result);
                result = easyMsRedisTemplate.listLeftRange(redisKey, redisIndexStart, redisIndexEnd);
            } else {
                List<String> strResultList = result.stream()
                        .map(easyMsRedisTemplate::getValueStr)
                        .collect(Collectors.toList());
                easyMsRedisTemplate.listRightPushAll(redisKey, strResultList);
                result = range(redisKey, redisIndexStart, redisIndexEnd, genericReturnClass);
            }
        }
        if (override) {
            easyMsRedisTemplate.listLeftTrim(redisKey, redisIndexStart, redisIndexEnd);
        }
        boolean isSetExpire = expire != null && expire > 0;
        if (isSetExpire) {
            easyMsRedisTemplate.expire(redisKey, expire, TimeUnit.MILLISECONDS);
        }
        return result;
    }

    private List<?> range(String redisKey, Long redisIndexStart, Long redisIndexEnd, Class<?> genericReturnClass) {
        if (genericReturnClass != null) {
            return easyMsRedisTemplate.listLeftRange(redisKey, redisIndexStart, redisIndexEnd, genericReturnClass);
        }
        return easyMsRedisTemplate.listLeftRange(redisKey, redisIndexStart, redisIndexEnd);
    }
}